/*
	this is a json library like cjson, auhtor:lutianming date:2021-08-03
*/

package ljson

import (
	"strconv"
)

const (
	Json_Invalid = 0
	Json_NIL     = 1 << 0
	Json_Bool    = 1 << 1
	Json_Number  = 1 << 2
	Json_String  = 1 << 3
	Json_Array   = 1 << 4
	Json_Object  = 1 << 5
)

type Json struct {
	next  *Json
	prev  *Json
	child *Json

	Type int8

	Valuebool   bool
	Valuestring string
	Valuenumber int64
	Valuefloat  float64

	Name string
}

type gbuffer struct {
	str    []byte
	length int
	offset int
}

func add_to_tail(node *Json, item *Json) {

	if node.child == nil {
		node.child = item
		item.next = item
		item.prev = item
		return
	}
	head := node.child
	prev := head.prev

	item.next = head
	item.prev = prev

	prev.next = item
	head.prev = item
}

func can_read(buffer *gbuffer, size int) bool {
	return buffer.offset+size <= buffer.length
}

func buffer_get_pos(buffer *gbuffer, b byte) int {
	for pos := buffer.offset; pos < buffer.length; pos++ {
		if buffer.str[pos] == b {
			return pos
		}
	}
	return -1
}

func buffer_skip_whitespace(buffer *gbuffer) {
	var pos int
	for pos = buffer.offset; pos < buffer.length; pos++ {
		chr := buffer.str[pos]
		if chr > 32 {
			break
		}
	}
	buffer.offset = pos
}

func bytescmp(buffer *gbuffer, size int, a string) bool {
	if can_read(buffer, size) == false {
		return false
	}
	b := []byte(a)
	for i := 0; i < size; i++ {
		pos := buffer.offset + i
		if buffer.str[pos] != b[i] {
			return false
		}
	}
	return true
}

func bytecmp(buffer *gbuffer, b byte) bool {
	if can_read(buffer, 1) == false {
		return false
	}
	pos := buffer.offset
	if buffer.str[pos] == b {
		return true
	}
	return false
}

func parse_nil(node *Json, buffer *gbuffer) bool {
	node.Type = Json_NIL
	buffer.offset += 4
	return true
}

func parse_bool(node *Json, buffer *gbuffer, size int) bool {
	node.Type = Json_Bool
	if size == 4 {
		node.Valuebool = true
	} else {
		node.Valuebool = false
	}
	buffer.offset += size
	return true
}

func parse_string(node *Json, buffer *gbuffer) bool {
	node.Type = Json_String
	buffer.offset++
	for pos := buffer.offset; pos < buffer.length; pos++ {
		if buffer.str[pos] == '"' {
			node.Valuestring = string(buffer.str[buffer.offset:pos])
			buffer.offset = pos + 1
			return true
		}
	}
	return false
}

func parse_number(node *Json, buffer *gbuffer) bool {
	node.Type = Json_Number
	for pos := buffer.offset; pos < buffer.length; pos++ {
		if buffer.str[pos] == ' ' || buffer.str[pos] == ',' || buffer.str[pos] == ']' || buffer.str[pos] == '}' {
			number, _ := strconv.ParseInt(string(buffer.str[buffer.offset:pos]), 10, 64)
			//numver_float, _ := strconv.ParseFloat(string(buffer.str[buffer.offset:pos]), 64)
			node.Valuenumber = number
			//node.Valuefloat = numver_float
			buffer.offset = pos
			return true
		}
	}
	return false
}

func parse_array(node *Json, buffer *gbuffer) bool {
	node.Type = Json_Array
	buffer.offset++
	buffer_skip_whitespace(buffer)

	for {
		if buffer.offset >= buffer.length {
			return false
		}
		if bytecmp(buffer, ']') {
			buffer.offset++
			return true
		} else {
			item := new(Json)
			if parse_value(item, buffer) == false {
				return false
			}
			add_to_tail(node, item)

			buffer_skip_whitespace(buffer)
			if bytecmp(buffer, ',') {
				buffer.offset++
				buffer_skip_whitespace(buffer)
			}
		}
	}
}

func parse_key(node *Json, buffer *gbuffer) bool {
	pos := buffer_get_pos(buffer, '"')
	if pos > buffer.offset {
		node.Name = string(buffer.str[buffer.offset:pos])
		buffer.offset = pos + 1
		return true
	}
	return false
}

func parse_object(node *Json, buffer *gbuffer) bool {
	node.Type = Json_Object
	buffer.offset++
	buffer_skip_whitespace(buffer)

	for {
		if buffer.offset >= buffer.length {
			return false
		}
		if bytecmp(buffer, '}') {
			buffer.offset++
			return true
		} else if bytecmp(buffer, '"') {
			item := new(Json)

			buffer.offset++
			if parse_key(item, buffer) == false {
				return false
			}
			pos := buffer_get_pos(buffer, ':')
			if pos < 0 {
				return false
			}
			buffer.offset = pos + 1
			if parse_value(item, buffer) == false {
				return false
			}
			add_to_tail(node, item)

			buffer_skip_whitespace(buffer)
			if bytecmp(buffer, ',') {
				buffer.offset++
				buffer_skip_whitespace(buffer)
			}
		} else {
			return false
		}
	}
}

func parse_value(node *Json, buffer *gbuffer) bool {
	buffer_skip_whitespace(buffer)
	if bytecmp(buffer, '{') {
		return parse_object(node, buffer)
	} else if bytecmp(buffer, '[') {
		return parse_array(node, buffer)
	} else if (buffer.str[buffer.offset] >= '0' && buffer.str[buffer.offset] <= '9') || buffer.str[buffer.offset] == '-' {
		return parse_number(node, buffer)
	} else if bytecmp(buffer, '"') {
		return parse_string(node, buffer)
	} else if bytescmp(buffer, 4, "null") {
		return parse_nil(node, buffer)
	} else if bytescmp(buffer, 5, "false") {
		return parse_bool(node, buffer, 5)
	} else if bytescmp(buffer, 4, "true") {
		return parse_bool(node, buffer, 4)
	}
	return false
}

func ParseString(str string) *Json {
	buffer := new(gbuffer)
	buffer.str = []byte(str)
	buffer.length = len(str)
	buffer.offset = 0

	node := new(Json)
	if parse_value(node, buffer) == false {
		print("ljson error: offset[", buffer.offset, "]\tnear:", string(buffer.str[buffer.offset:]), "\n")
		return nil
	}
	return node
}

func ParseBytes(bytes []byte, n int) *Json {
	buffer := new(gbuffer)
	buffer.str = bytes
	buffer.length = n
	buffer.offset = 0

	node := new(Json)
	if parse_value(node, buffer) == false {
		print("ljson error: offset[", buffer.offset, "]\tnear:", string(buffer.str[buffer.offset:]), "\n")
		return nil
	}
	return node
}

func print_need_buffer(buffer *gbuffer, count int) {
	if buffer.length-buffer.offset < count {
		newlen := buffer.length * 2
		newbyte := make([]byte, newlen)
		copy(newbyte, buffer.str)
		buffer.str = newbyte
		buffer.length = newlen
	}
}

func print_nil(node *Json, buffer *gbuffer) {
	print_need_buffer(buffer, 4)
	value := []byte("null")
	for i := 0; i < 4; i++ {
		buffer.str[buffer.offset+i] = value[i]
	}
	buffer.offset += 4
}

func print_bool(node *Json, buffer *gbuffer) {
	if node.Valuebool {
		print_need_buffer(buffer, 4)
		value := []byte("true")
		for i := 0; i < 4; i++ {
			buffer.str[buffer.offset+i] = value[i]
		}
		buffer.offset += 4
	} else {
		print_need_buffer(buffer, 5)
		value := []byte("false")
		for i := 0; i < 5; i++ {
			buffer.str[buffer.offset+i] = value[i]
		}
		buffer.offset += 5
	}
}

func print_string(node *Json, buffer *gbuffer) {
	print_need_buffer(buffer, 1)
	buffer.str[buffer.offset] = '"'
	buffer.offset++

	value := []byte(node.Valuestring)
	len := len(node.Valuestring)
	print_need_buffer(buffer, len)
	for i := 0; i < len; i++ {
		buffer.str[buffer.offset+i] = value[i]
	}
	buffer.offset += len

	print_need_buffer(buffer, 1)
	buffer.str[buffer.offset] = '"'
	buffer.offset++
}

func print_number(node *Json, buffer *gbuffer) {
	valuestr := strconv.FormatInt(node.Valuenumber, 10)
	value := []byte(valuestr)
	len := len(valuestr)
	print_need_buffer(buffer, len)
	for i := 0; i < len; i++ {
		buffer.str[buffer.offset+i] = value[i]
	}
	buffer.offset += len
}

func print_key(node *Json, buffer *gbuffer) {
	print_need_buffer(buffer, 1)
	buffer.str[buffer.offset] = '"'
	buffer.offset++

	value := []byte(node.Name)
	len := len(node.Name)
	print_need_buffer(buffer, len)
	for i := 0; i < len; i++ {
		buffer.str[buffer.offset+i] = value[i]
	}
	buffer.offset += len

	print_need_buffer(buffer, 1)
	buffer.str[buffer.offset] = '"'
	buffer.offset++
}

func print_array(node *Json, buffer *gbuffer) {
	print_need_buffer(buffer, 1)
	buffer.str[buffer.offset] = '['
	buffer.offset++

	var head *Json = node.child
	var item *Json = head
	for {
		if item == nil {
			break
		}
		print_value(item, buffer)
		if item.next != head {
			print_need_buffer(buffer, 1)
			buffer.str[buffer.offset] = ','
			buffer.offset++
		}
		item = item.next
		if item == head {
			break
		}
	}
	print_need_buffer(buffer, 1)
	buffer.str[buffer.offset] = ']'
	buffer.offset++
}

func print_object(node *Json, buffer *gbuffer) {
	print_need_buffer(buffer, 1)
	buffer.str[buffer.offset] = '{'
	buffer.offset++

	var head *Json = node.child
	var item *Json = head
	for {
		if item == nil {
			break
		}
		print_key(item, buffer)
		print_need_buffer(buffer, 1)
		buffer.str[buffer.offset] = ':'
		buffer.offset++
		print_value(item, buffer)
		if item.next != head {
			print_need_buffer(buffer, 1)
			buffer.str[buffer.offset] = ','
			buffer.offset++
		}
		item = item.next
		if item == head {
			break
		}
	}
	print_need_buffer(buffer, 1)
	buffer.str[buffer.offset] = '}'
	buffer.offset++
}

func print_value(node *Json, buffer *gbuffer) {
	if node.Type == Json_NIL {
		print_nil(node, buffer)
	} else if node.Type == Json_Bool {
		print_bool(node, buffer)
	} else if node.Type == Json_String {
		print_string(node, buffer)
	} else if node.Type == Json_Number {
		print_number(node, buffer)
	} else if node.Type == Json_Array {
		print_array(node, buffer)
	} else if node.Type == Json_Object {
		print_object(node, buffer)
	}
}

func (node *Json) String() string {
	buffer := new(gbuffer)
	buffer.str = make([]byte, 5120)
	buffer.length = 5120
	buffer.offset = 0
	print_value(node, buffer)
	return string(buffer.str[:buffer.offset])
}

func (node *Json) Bytes() ([]byte, int) {
	buffer := new(gbuffer)
	buffer.str = make([]byte, 5120)
	buffer.length = 5120
	buffer.offset = 0
	print_value(node, buffer)
	return buffer.str, buffer.offset
}

func (node *Json) IsBool() bool {
	if node.Type == Json_Bool {
		return true
	}
	return false
}

func (node *Json) IsString() bool {
	if node.Type == Json_String {
		return true
	}
	return false
}

func (node *Json) IsNumber() bool {
	if node.Type == Json_Number {
		return true
	}
	return false
}

func (node *Json) IsArray() bool {
	if node.Type == Json_Array {
		return true
	}
	return false
}

func (node *Json) IsObject() bool {
	if node.Type == Json_Object {
		return true
	}
	return false
}

func CreateBoolObject(value bool) *Json {
	node := new(Json)
	node.Type = Json_Bool
	node.Valuebool = value
	return node
}

func CreateStringObject(value string) *Json {
	node := new(Json)
	node.Type = Json_String
	node.Valuestring = value
	return node
}

func CreateNumberObject(value int64) *Json {
	node := new(Json)
	node.Type = Json_Number
	node.Valuenumber = value
	return node
}

func CreateObject() *Json {
	node := new(Json)
	node.Type = Json_Object
	return node
}

func CreateArray() *Json {
	node := new(Json)
	node.Type = Json_Array
	return node
}

func (node *Json) ObjectAddBool(key string, value bool) {
	item := CreateBoolObject(value)
	item.Name = key
	add_to_tail(node, item)
}

func (node *Json) ObjectAddString(key string, value string) {
	item := CreateStringObject(value)
	item.Name = key
	add_to_tail(node, item)
}

func (node *Json) ObjectAddNumber(key string, value int64) {
	item := CreateNumberObject(value)
	item.Name = key
	add_to_tail(node, item)
}

func (node *Json) ObjectAddItem(key string, item *Json) {
	item.Name = key
	add_to_tail(node, item)
}

func (node *Json) ArrayAddBool(value bool) {
	item := CreateBoolObject(value)
	add_to_tail(node, item)
}

func (node *Json) ArrayAddString(value string) {
	item := CreateStringObject(value)
	add_to_tail(node, item)
}

func (node *Json) ArrayAddNumber(value int64) {
	item := CreateNumberObject(value)
	add_to_tail(node, item)
}

func (node *Json) ArrayAddItem(item *Json) {
	add_to_tail(node, item)
}

func (node *Json) GetSize() int {
	size := 0
	head := node.child
	if head == nil {
		return size
	}
	var item *Json = head
	for {
		size++
		if item.next == head {
			break
		}
		item = item.next
	}
	return size
}

func (node *Json) ArrayGetItem(index int) *Json {
	head := node.child
	if head == nil {
		return nil
	}
	var item *Json = head
	for i := 0; i < index; i++ {
		item = item.next
	}
	return item
}

func (node *Json) ObjectGetItem(key string) *Json {
	head := node.child
	if head == nil {
		return nil
	}
	var item *Json = head
	for {
		if item.Name == key {
			return item
		}
		if item.next == head {
			break
		}
		item = item.next
	}
	return nil
}

func (node *Json) ItemNext(iter *Json) *Json {
	if iter == nil {
		return node.child
	}
	if iter.next == node.child {
		return nil
	}
	return iter.next
}
